package com.chebaohui.iqs.service.impl;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.net.InetAddress;
import java.util.Map;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.ModelAndView;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import com.alibaba.druid.support.logging.Log;
import com.alibaba.druid.support.logging.LogFactory;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.chebaohui.iqs.config.CompanyValue;
import com.chebaohui.iqs.config.PayCode;
import com.chebaohui.iqs.config.ResultCode;
import com.chebaohui.iqs.entity.Paramter;
import com.chebaohui.iqs.entity.Result;
import com.chebaohui.iqs.entity.base.Pay;
import com.chebaohui.iqs.entity.db.DbParam;
import com.chebaohui.iqs.entity.pay.Pays;
import com.chebaohui.iqs.sdk.OkHttp;
import com.chebaohui.iqs.sdk.util.RandomStringGenerator;
import com.chebaohui.iqs.service.DBService;
import com.chebaohui.iqs.service.PayService;
import com.chebaohui.iqs.service.impl.deal.pay.PaysCheck;
import com.chebaohui.iqs.service.impl.deal.util.StringUtil;
import com.chebaohui.iqs.service.impl.deal.util.TimeBase;
import com.chebaohui.iqs.service.impl.wxutil.AuthUtil;
import com.chebaohui.iqs.service.impl.wxutil.WXPayUtil;
import com.chebaohui.iqs.service.impl.wxutil.WXUtil;
import com.chebaohui.iqs.sign.SignParam;
import com.chebaohui.iqs.sign.SignUtil;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;

@Service
public class PayServiceImpl implements PayService {
	/**
	 * log:日志输出
	 */
	private static Log log = LogFactory.getLog(PayServiceImpl.class);

	// private final String APPID ="wx7191e033bf7fd7b1";// // 公众号APPID
	// private final String AppSecret = "fc7214f093bd4a6191d2b0bb783b0acb";//
	// private final String API_KEY = "08Ll5P75qP54M5vGt1CUx94rRoERHhHx";//
	// private final String mch_id ="1427174002";//
	private final String APPID = "wx4cbd71ca5b3f1248";// 公众号APPID
	private final String AppSecret = "433b36e4517a7f1b7bfa1cff5b4042a1";// 公众号secret
	private final String API_KEY = "iKO7beaxPn2GOiRCofpf2WwXKVdPpw47";
	private final String mch_id = "1279978101";// 微信支付分配的商户号

	@Autowired
	private DBService db;

	@Override
	public ModelAndView errorView(int type) {
		switch (type) {
		case 1:
			return error("无此订单信息");
		case 2:
			return error("支付方式错误");
		case 3:
			return error("支付异常");
		case 4:
			return error("订单支付超时，请重新生成订单");
		case 5:
			return error("订单已支付");
		default:
			return error("支付失败..");
		}
	}

	@Override
	public String wxPay(String orderSid, final HttpServletRequest request) {
		try {
			log.info("订单:" + orderSid + ",微信支付调起....");
			DbParam sql = new DbParam();
			sql.setcType("pay");
			// sql.setNumType("one");
			sql.setSpare(orderSid);
			List<DbParam> res = db.query(sql);
			if (null == res || res.isEmpty() || res.size() < 1) {
				log.error(orderSid + ",此订单不存在");
				return CompanyValue.PATH + "/pay/error?type=1";
			}

			JSONObject pays = JSONObject.parseObject(res.get(0).getDatas());

			String createIp = getClientIP(request);
			if (createIp.indexOf(",") > -1) {
				createIp = createIp.split(",")[0];
			}
			pays.put("userIp", createIp);

			long reserve = Long.valueOf(res.get(0).getReserve())
					+ 1000 * 60 * Integer.valueOf(pays.getString("timeOut"));
			if (new Date().getTime() > reserve) {
				log.error(orderSid + ",订单支付超时，请重新生成订单");
				return CompanyValue.PATH + "/pay/error?type=4";
			}

			BigDecimal amount = new BigDecimal(pays.getString("amount"));
			amount = amount.divide(new BigDecimal("100"));
			if (!PayCode.WEIXIN.getCode().equals(pays.getString("payType"))) {
				log.error(orderSid + ",支付方式错误：" + pays.getString("payType"));
				return CompanyValue.PATH + "/pay/error?type=2";
			}
			if ("three".equals(res.get(0).getNumType())) {
				log.error(orderSid + ",订单已支付");
				return CompanyValue.PATH + "/pay/error?type=5";
			}

			if ("one".equals(res.get(0).getNumType())) {
				DbParam dbp = res.get(0);
				dbp.setNumType("two");
				dbp.setDatas(pays.toJSONString());
				db.undate(dbp);
			}
			/** 微信授权路径组装 */
			return "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + APPID // 公众号APPID
					+ "&redirect_uri=" + URLEncoder.encode(CompanyValue.PATH + "/pay/wx/callBack", "utf-8") // 回调路径
					+ "&response_type=code" + "&scope=snsapi_base" // 微信授权类型
					+ "&state=" + orderSid // 订单号
					+ "#wechat_redirect ";
		} catch (Exception e) {
			log.error("订单:" + orderSid + ",微信支付调起发生异常错误:" + e.getMessage());
			return CompanyValue.PATH + "/pay/error?type=3";
		}
	}

	@Override
	public ModelAndView backToPrice(final HttpServletRequest request) {
		ModelAndView view = new ModelAndView("weixin/pay");
		DbParam sql = new DbParam();
		try {
			log.info("微信授权回调参数：" + JSONObject.toJSONString(request.getParameterMap()));
			String code = request.getParameter("code");
			if (null == code || "".equals(code.trim())) {
				view.addObject("state", false);
				view.addObject("code", false);
				view.addObject("msg", "微信授权code失败");
				return view;
			}
			String state = request.getParameter("state");
			sql.setcType("pay");
			sql.setNumType("two");
			sql.setSpare(state);

			List<DbParam> res = db.query(sql);
			if (null == res || res.isEmpty() || res.size() < 1) {
				log.error(state + ",此订单不存在");
				view.addObject("state", false);
				view.addObject("code", false);
				view.addObject("msg", "微信支付后台数据库查询错误");
				return view;
			}

			JSONObject pays = JSONObject.parseObject(res.get(0).getDatas());
			// DbParam dbp=res.get(0);

			view.addObject("code", true);
			view.addObject("successUrl", pays.getString("successUrl"));
			view.addObject("errorUrl", pays.getString("failUrl"));

			// 微信授权后根据code获取用户信息--路径组装
			String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + APPID // 公众号APPID
					+ "&secret=" + AppSecret// 公众号secret
					+ "&code=" + code + "&grant_type=authorization_code";
			JSONObject obj = AuthUtil.doGet(url);
			log.info("微信授权后根据code获取用户信息--请求返回 :" + obj);
			String openid = obj.getString("openid");
			if (null == openid || "".equals(openid.trim())) {
				view.addObject("state", false);
				view.addObject("code", false);
				view.addObject("url", pays.getString("failUrl"));
				view.addObject("msg", "微信授权失败");
				return view;
			}

			String xmlObj = wxPriceParam(pays, openid, state, Long.valueOf(res.get(0).getReserve()));
			log.info("微信统一下单请求参数xml:" + xmlObj);

			String xmlRes = wxPrice(xmlObj);
			log.info("微信统一下单返回xml:" + xmlRes);

			if (!WXPayUtil.isSignatureValid(xmlRes, API_KEY)) {// 验证签名
				view.addObject("state", false);
				view.addObject("msg", "微信:签名验证失败");
				return view;
			}

			Map<String, String> resultMap = WXPayUtil.xmlToMap(xmlRes);
			log.info("微信统一下单返回map:" + xmlRes);

			if ("FAIL".equals(resultMap.get("result_code"))) {
				view.addObject("state", false);
				view.addObject("msg", "微信:" + resultMap.get("err_code"));
				return view;
			}
			if ("FAIL".equals(resultMap.get("return_code"))) {
				view.addObject("state", false);
				view.addObject("msg", "微信:" + resultMap.get("return_msg"));
				return view;
			}

			Map<String, String> data = new HashMap<String, String>();
			data.put("appId", APPID);// 公众号id
			long t = new Date().getTime();
			t = t / 1000;
			data.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000L));// 时间戳
			data.put("nonceStr", RandomStringGenerator.getRandomStringByLength(32));// 随机字符串
			data.put("package", "prepay_id=" + resultMap.get("prepay_id"));// 订单详情扩展字符串
			data.put("signType", "MD5");// 签名方式
			data.put("paySign", WXPayUtil.generateSignature(data, API_KEY));// 签名

			view.addObject("state", true);

			view.addObject("appId", APPID);// 公众号id
			view.addObject("timeStamp", data.get("timeStamp"));// 时间戳
			view.addObject("nonceStr", data.get("nonceStr"));// 随机字符串
			view.addObject("pkg", data.get("package"));// 订单详情扩展字符串
			view.addObject("signType", data.get("signType"));// 签名方式
			view.addObject("paySign", data.get("paySign"));// 签名
			return view;
		} catch (Exception e) {
			log.error("收银台<-->微信后台错误" + e.getMessage());
			view.addObject("code", false);
			view.addObject("state", false);
			view.addObject("msg", "收银台<-->微信后台错误:" + e.getMessage());
			return view;
		}
	}

	/**
	 * "微信统一下单请求发起
	 * 
	 * @param xmlObj
	 * @return
	 */
	private String wxPrice(String xmlObj) {
		HttpClient client = new HttpClient();
		PostMethod myPost = new PostMethod("https://api.mch.weixin.qq.com/pay/unifiedorder");
		client.getParams().setSoTimeout(300 * 1000);
		String result = null;
		try {
			myPost.setRequestEntity(new StringRequestEntity(xmlObj, "text/xml", "utf-8"));
			int statusCode = client.executeMethod(myPost);
			if (statusCode == HttpStatus.SC_OK) {
				// 使用流的方式解析微信服务器返回的xml结构的字符串

				BufferedInputStream bis = new BufferedInputStream(myPost.getResponseBodyAsStream());
				byte[] bytes = new byte[1024];
				ByteArrayOutputStream bos = new ByteArrayOutputStream();
				int count = 0;
				while ((count = bis.read(bytes)) != -1) {
					bos.write(bytes, 0, count);
				}
				byte[] strByte = bos.toByteArray();
				result = new String(strByte, 0, strByte.length, "utf-8");
				bos.close();
				bis.close();
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
		/** 需要释放掉、关闭连接 */
		myPost.releaseConnection();
		client.getHttpConnectionManager().closeIdleConnections(0);
		return result;
	}

	@Override
	public void priceBack(final HttpServletRequest request, final HttpServletResponse response) {
		try {
			InputStream inStream = request.getInputStream();
			ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
			byte[] buffer = new byte[1024];
			int len = 0;
			while ((len = inStream.read(buffer)) != -1) {
				outSteam.write(buffer, 0, len);
			}
			log.info("~~~~~~~~~~~~~~~~付款成功~~~~~~~~~");
			outSteam.close();
			inStream.close();
			/** 支付成功后，微信回调返回的信息 */
			String result = new String(outSteam.toByteArray(), "utf-8");
			Map<String, String> map = WXPayUtil.xmlToMap(result);
			log.info("微信支付回调-参数:" + JSONObject.toJSONString(map));

			if (map.get("result_code").toString().equalsIgnoreCase("SUCCESS")) {
				OutputStream outputStream = null;
				try {
					outputStream = response.getOutputStream();
					outputStream.flush();
					outputStream.write(setXML("SUCCESS", "").getBytes());
				} catch (Exception e) {
					e.printStackTrace();
					log.error("OutputStream写入异常:" + e.getMessage());
				} finally {
					try {
						outputStream.close();
					} catch (Exception e) {
						e.printStackTrace();
						log.error("outputStream关闭异常:" + e.getMessage());
					}
				}
				log.info("微信支付回调返回：" + setXML("SUCCESS", ""));
			}

			if (WXPayUtil.isSignatureValid(map, API_KEY)) {
				if (map.get("result_code").toString().equalsIgnoreCase("SUCCESS")) {
					log.info("付款成功---开始业务处理");
					returnBackYewu(map, "001");
				} else {
					log.error("付款失败---开始业务处理");
					returnBackYewu(map, "003");
				}
			} else {
				log.info("微信支付回调--签名错误");
			}

		} catch (Exception e) {
			log.error("微信支付回调--异常:" + e.getMessage());
		}
	}

	/**
	 * 回调业务方逻辑处理
	 * 
	 * @param map
	 */
	private void returnBackYewu(Map<String, String> map, String code) {
		try {
			DbParam sql = new DbParam();
			sql.setcType("pay");
			sql.setNumType("two");
			sql.setSpare(map.get("out_trade_no"));

			List<DbParam> res = db.query(sql);
			if (null == res || res.isEmpty() || res.size() < 1) {
				log.error(sql.getSpare() + ",此订单数据库不存在");
				return;
			}
			JSONObject pays = JSONObject.parseObject(res.get(0).getDatas());
			sql = res.get(0);
			sql.setNumType("three");
			pays.put("payResult", JSONObject.parseObject(JSONObject.toJSONString(map)));

			BigDecimal amount = new BigDecimal(map.get("total_fee"));
			amount = amount.divide(new BigDecimal("100"));

			pays.put("backYeWu", returnBack(sql.getSpare(), amount.toString(), code, pays.getString("backUrl"), "2"));
			sql.setDatas(JSONObject.toJSONString(pays));
			db.undate(sql);
		} catch (Exception e) {
			e.printStackTrace();
			log.error("业务方回调异常:" + e.getMessage());
		}
	}

	/**
	 * 业务方回调
	 * 
	 * @param url
	 * @param orderId
	 * @param orderStatus
	 * @return
	 */
	private String returnBack(String orderSid, String amount, String code, String url, String payType) {
		try {
			// Map<String, String> map = new HashMap<String, String>();
			JSONObject map = new JSONObject();
			map.put("sign",
					SignUtil.encryption(new SimpleDateFormat("yyyy-MM-dd").format(new Date()), SignParam.CHANNEL_CODE));
			map.put("payType", payType);// 支付方式代码，1直连、2微信、3银联
			map.put("incomePrice", amount);// 支付金额，单位：元
			map.put("orderSn", orderSid);// 订单号（后台的订单号）
			map.put("code", code);// 001,支付成功;002,核保成功（可以通知前台打单了）;003,支付失败;004,核保失败
			log.info("业务方回调参数:" + JSONObject.toJSONString(map));
			String res = OkHttp.postByJson(url, map.toJSONString());
			log.info("业务方回调返回:" + res);
			return res;
		} catch (Exception e) {
			return "回调异常:" + e.getMessage();
		}
	}

	/**
	 * 发送xml格式数据到微信服务器 告知微信服务器回调信息已经收到。
	 * 
	 * @param return_code
	 * @param return_msg
	 * @return
	 */
	private String setXML(String return_code, String return_msg) {
		return "<xml><return_code><![CDATA[" + return_code + "]]></return_code><return_msg><![CDATA[" + return_msg
				+ "]]></return_msg></xml>";
	}

	/**
	 * 微信支付统一下单参数设定
	 * 
	 * @param pay
	 * @param openid
	 * @return
	 * @throws Exception
	 */
	private String wxPriceParam(JSONObject pays, String openid, String sid, long timeO) throws Exception {
		Map<String, String> wxs = new HashMap<>();
		wxs.put("appid", APPID);// 微信支付分配的公众账号ID
		wxs.put("mch_id", mch_id);// 微信支付分配的商户号
		wxs.put("nonce_str", WXUtil.getNonceStr());
		wxs.put("body", StringUtil.isNulls(pays.getString("subject")) ? sid : pays.getString("subject"));// 商品描述
		wxs.put("out_trade_no", sid);// 商户订单号
		wxs.put("total_fee", pays.getString("amount"));// 标价金额--分
		wxs.put("spbill_create_ip", pays.getString("userIp"));// 终端IP--APP和网页支付提交用户端ip，Native支付填调用微信支付API的机器IP
		wxs.put("time_start", TimeBase.getDay(TimeBase.time_sdf, 0));// 交易起始时间

		long time = timeO + (1000 * 60 * Integer.valueOf(pays.getString("timeOut")));
		wxs.put("time_expire", new SimpleDateFormat(TimeBase.time_sdf).format(new Date(time)));// 交易结束时间
		wxs.put("notify_url", CompanyValue.PATH + "/pay/wx/priceBack");// 支付后通知地址
		wxs.put("trade_type", "JSAPI");// 交易类型
		wxs.put("openid", openid);// 用户标识

		return WXPayUtil.generateSignedXml(wxs, API_KEY);
	}

	@Override
	public Result<Pay> urlPay(Paramter<Pays> param) {
		if (!"true".equals(PaysCheck.sign(param.getData())))
			return new Result<>(ResultCode.L02, PaysCheck.sign(param.getData()));
		if (0 == param.getData().getTimeOut())
			param.getData().setTimeOut(15);
		log.info("订单:" + param.getData().getOrderSid() + ",支付类型:" + param.getData().getPayType() + ",登记支付发起....");
		DbParam sql = new DbParam();
		sql.setcType("pay");
		sql.setSpare(param.getData().getOrderSid());

		try {
			String payUrl = CompanyValue.PATH;
			if (PayCode.WEIXIN.getCode().equals(param.getData().getPayType()))
				payUrl = payUrl + "/pay/wx?orderSid=";
			else if (PayCode.UNION.getCode().equals(param.getData().getPayType())) {
				payUrl = payUrl + "/china/newUnion/money?orderSid=";
			} else
				return new Result<Pay>(ResultCode.N07, "支付类型错误:" + param.getData().getPayType());

			List<DbParam> res = db.query(sql);
			if (null == res || res.isEmpty() || res.size() < 1) {
				sql.setNumType("one");
				String payBeginTime = new Date().getTime() + "";
				sql.setReserve(payBeginTime);
				sql.setDatas(JSON.toJSONString(param.getData()));
				db.add(sql);
				return new Result<Pay>(ResultCode.SUCCESS, new Pay(payUrl + param.getData().getOrderSid(),
						new SimpleDateFormat(TimeBase.time_default).format(new Date(Long.valueOf(payBeginTime)))));
			}

			log.info("订单:" + param.getData().getOrderSid() + ",支付类型:" + param.getData().getPayType()
					+ ",此订单已登记过或已支付了。。:" + res.get(0).getNumType());

			if ("one".equals(res.get(0).getNumType())) {
				if (new Date().getTime() >= Long.valueOf(sql.getReserve()) + 15 * 60 * 1000) {
					return new Result<Pay>(ResultCode.N07, "订单已过期，请重新生成订单");
				} else {
					// return new Result<Pay>(ResultCode.N07, "订单已生成，请支付");
					return new Result<Pay>(ResultCode.SUCCESS,
							new Pay(payUrl + param.getData().getOrderSid(), new SimpleDateFormat(TimeBase.time_default)
									.format(new Date(Long.valueOf(res.get(0).getReserve())))));
				}
			} else if ("two".equals(res.get(0).getNumType())) {
				// return new Result<Pay>(ResultCode.N07, "订单支付中");
				return new Result<Pay>(ResultCode.SUCCESS,
						new Pay(payUrl + param.getData().getOrderSid(), new SimpleDateFormat(TimeBase.time_default)
								.format(new Date(Long.valueOf(res.get(0).getReserve())))));
			} else {
				return new Result<Pay>(ResultCode.N07, "订单已支付");
			}

		} catch (Exception e) {
			log.error("订单:" + param.getData().getOrderSid() + ",支付类型:" + param.getData().getPayType() + ",登记支付发起出现异常:"
					+ e.getMessage());
			return new Result<Pay>(ResultCode.N02, "登记支付出现异常:" + e.getMessage());
		} finally {
			log.info("订单:" + param.getData().getOrderSid() + ",支付类型:" + param.getData().getPayType() + ",登记支付发起结束");
		}
	}

	/** 错误页面 **/
	private ModelAndView error(String msg) {
		ModelAndView view = new ModelAndView("error");
		view.addObject("error_msg", msg);
		return view;
	}

	/**
	 * 获取客户端IP
	 * 
	 * @param httpservletrequest
	 * @return
	 */
	private static String getClientIP(final HttpServletRequest httpservletrequest) {
		if (httpservletrequest == null)
			return null;
		String s = httpservletrequest.getHeader("X-Forwarded-For");
		if (s == null || s.length() == 0 || "unknown".equalsIgnoreCase(s))
			s = httpservletrequest.getHeader("Proxy-Client-IP");
		if (s == null || s.length() == 0 || "unknown".equalsIgnoreCase(s))
			s = httpservletrequest.getHeader("WL-Proxy-Client-IP");
		if (s == null || s.length() == 0 || "unknown".equalsIgnoreCase(s))
			s = httpservletrequest.getHeader("HTTP_CLIENT_IP");
		if (s == null || s.length() == 0 || "unknown".equalsIgnoreCase(s))
			s = httpservletrequest.getHeader("HTTP_X_FORWARDED_FOR");
		if (s == null || s.length() == 0 || "unknown".equalsIgnoreCase(s))
			s = httpservletrequest.getRemoteAddr();
		if ("127.0.0.1".equals(s) || "0:0:0:0:0:0:0:1".equals(s))
			try {
				s = InetAddress.getLocalHost().getHostAddress();
			} catch (Exception unknownhostexception) {
			}

		return s;
	}

}
